/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#include "MkBase64.h"

static const char base64tab[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

Uint32 MkBase64Encode(const MkString &strOrig, MkString &strEncode)
{
    strEncode.clear();
    Uint8 *tempBuff = new Uint8[3];
    size_t srcsize = strOrig.size();
    const char *src = strOrig.c_str();
    //取到最后剩几个数据
    int lastNumb = srcsize % 3;

    //分为三组处理
    size_t i;
    for (i = 0; i < (srcsize / 3); i++) {
        memset(tempBuff, 0x00, 3);
        memcpy(tempBuff, (src + i * 3), 3);

        strEncode += base64tab[(tempBuff[0] >> 2)];
        strEncode += base64tab[(((tempBuff[0] & 0x03) << 4) | (tempBuff[1] >> 4))];
        strEncode += base64tab[(((tempBuff[1] & 0x0f) << 2) | (tempBuff[2] >> 6))];
        strEncode += base64tab[(tempBuff[2] & 0x3f)];
    }
    //剩余不够一组的数据处理
    if (lastNumb == 1) {
        memset(tempBuff, 0x00, 3);
        memcpy(tempBuff, (src + i * 3), 1);
        strEncode += base64tab[(tempBuff[0] >> 2)];
        strEncode += base64tab[(tempBuff[0] & 0x03) << 4];
        strEncode.append("==");
    }
    if (lastNumb == 2) {
        memset(tempBuff, 0x00, 2);
        memcpy(tempBuff, (src + i * 3), 2);
        strEncode += base64tab[(tempBuff[0] >> 2)];
        strEncode += base64tab[(((tempBuff[0] & 0x03) << 4) | (tempBuff[1] >> 4))];
        strEncode += base64tab[((tempBuff[1] & 0x0f) << 2)];
        strEncode.append("=");
    }
    //释放内存
    delete[] tempBuff;
    return NoneError;
}

static const Uint8 un_b64[] = {
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62,  255, 255, 255, 63,
    52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  255, 255, 255, 255, 255, 255,
    255, 0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,
    15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  255, 255, 255, 255, 255,
    255, 26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,
    41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
};

#define UNSIG_CHAR_PTR(x) ((const Uint8 *)(x))
Uint32 MkBase64Decode(const MkString &strIn, MkString &strDecode)
{
    const Uint8 *cp;
    size_t     count;
    int     ch0;
    int     ch1;
    int     ch2;
    int     ch3;

    /*
    * Sanity check.
    */
    size_t len = strIn.size();
    if (len % 4) {
        return InvalidParams;
    }
    const char *in = strIn.c_str();

#define INVALID     0xff

    Uint8 *out = (Uint8 *)malloc(len + 1);
    int out_index = 0;
    for (cp = UNSIG_CHAR_PTR(in), count = 0; count < len; count += 4) {
        if ((ch0 = un_b64[*cp++]) == INVALID
            || (ch1 = un_b64[*cp++]) == INVALID) {
            free(out);
            return InvalidParams;
        }

        out[out_index++] = ch0 << 2 | ch1 >> 4;
        if ((ch2 = *cp++) == '=')
            break;
        if ((ch2 = un_b64[ch2]) == INVALID) {
            return InvalidParams;
        }
        out[out_index++] = ch1 << 4 | ch2 >> 2;
        if ((ch3 = *cp++) == '=')
            break;
        if ((ch3 = un_b64[ch3]) == INVALID) {
            return InvalidParams;
        }
        out[out_index++] = ch2 << 6 | ch3;
    }

    out[out_index] = 0;
    strDecode = MkString((char *)out, out_index);
    free(out);
    return NoneError;
}

